﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using StructureMap;
using StructureMap.Configuration.DSL.Expressions;
using StructureMap.Graph;
using System.Collections;
using System.IO;

namespace NetworkIndexer.CoreExtensions.ServiceLocation
{
    public static class ServiceLocator
    {
        static ServiceLocator()
        {
            Configure(delegate(ConfigurationExpression config)
            {                
                config.Scan(new Action<IAssemblyScanner>(ServiceLocator.ScanForServiceLocationConfigurations));
            });
            IList<IServiceLocatorConfiguration> serviceLocatorConfigurations = ObjectFactory.GetAllInstances<IServiceLocatorConfiguration>();
            Configure(config => ApplyServiceLocationConfigurations(serviceLocatorConfigurations, config));
        }

        private static void ApplyServiceLocationConfigurations(IEnumerable<IServiceLocatorConfiguration> serviceLocatorConfigurations, ConfigurationExpression config)
        {
            foreach (IServiceLocatorConfiguration configuration in serviceLocatorConfigurations)
            {
                configuration.Configure(config);
            }
        }

        private static void Configure(Action<ConfigurationExpression> configure)
        {
            ObjectFactory.Configure(configure);
        }

        public static void ContextSensitiveType<T>(this ConfigurationExpression configure) where T : class
        {
            //TODO: Get this working

            //configure.ForRequestedType<T>().get_TheDefault().get_Is().ConstructedBy(delegate(IContext context)
            //{
            //    Stack<string> stack = context.GetInstance<ServiceLocatorContext>().GetStack();
            //    foreach (string str in stack)
            //    {
            //        T local = context.TryGetInstance<T>(str);
            //        if (local != null)
            //        {
            //            return local;
            //        }
            //    }
            //    return default(T);
            //});
        }

        public static CreatePluginFamilyExpression<TBaseType> ContextSenstiveImplementation<TBaseType, TConcreteType>(this ConfigurationExpression configure, string contextName) where TConcreteType : TBaseType
        {
            return configure.ForRequestedType<TBaseType>().AddInstances(delegate(IInstanceExpression<TBaseType> x)
            {
                x.OfConcreteType<TConcreteType>().WithName(contextName);
            });
        }

        public static IDisposable EnterContext(string context)
        {
            return EnterContext(context, null);
        }

        public static IDisposable EnterContext(string context, object contextArguments)
        {
            IDisposable disposable = GetInstance<ServiceLocatorContext>().EnterContext(context);
            //TODO:
            return disposable;
        }

        

        public static IList<T> GetAllInstances<T>()
        {
            return ObjectFactory.GetAllInstances<T>();
        }

        public static IList GetAllInstances(Type type)
        {
            return ObjectFactory.GetAllInstances(type);
        }

        public static T GetInstance<T>()
        {
            return ObjectFactory.GetInstance<T>();
        }

        public static T GetInstance<T>(string name)
        {
            return ObjectFactory.GetNamedInstance<T>(name);
        }

        public static object GetInstance(Type type)
        {
            return ObjectFactory.GetInstance(type);
        }

        public static void IncludeConfigFile(this ConfigurationExpression configure, string configName)
        {
            string path = string.Format(@"{0}\{1}.config", AppDomain.CurrentDomain.BaseDirectory, configName);
            if (File.Exists(path))
            {
                configure.AddConfigurationFromXmlFile(path);
            }
        }

        private static void ScanForServiceLocationConfigurations(IAssemblyScanner scan)
        {
            string privateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
            if (!string.IsNullOrEmpty(privateBinPath))
            {
                string[] strArray = privateBinPath.Split(new char[] { ';' });
                foreach (string str2 in strArray)
                {
                    scan.AssembliesFromPath(str2);
                }
            }
            else
            {
                scan.AssembliesFromPath(AppDomain.CurrentDomain.BaseDirectory);
            }
            scan.AddAllTypesOf<IServiceLocatorConfiguration>();
        }

        public static T TryGetInstance<T>(string name)
        {
            return ObjectFactory.TryGetInstance<T>(name);
        }

        public static ServiceLocatorContext Context
        {
            get
            {
                return ObjectFactory.GetInstance<ServiceLocatorContext>();
            }
        }
    }
}
